// ZoomBarPlus.cpp : Defines the entry point for the DLL application.
//

#include <list>
#include <algorithm>
#include <cctype>
#include "stdafx.h"
#include "LED.h"

#define REGISTRY_SETTINGS _T("\\Services\\ZoomBarPlus")

TCHAR g_szWindowClass[MAX_PATH] = _T("HTCSmartTouchDll");
HINSTANCE g_hInstance = NULL;
HANDLE g_hThread = NULL;
HWND g_hSmartTouch = NULL;
WNDPROC g_origWndProc = NULL;

BYTE g_key1Tap   = 0x00;
BYTE g_key2Tap   = 0x0D;
BYTE g_keyLTap   = 0x08;
BYTE g_keyRSwipe = 0x28;
BYTE g_keyLSwipe = 0x26;
UINT g_doubleTapInterval = 350;
UINT g_longTapInterval = 650;
BOOL g_vibeOnTap = TRUE;
BOOL g_vibeOnSwipe = FALSE;
UINT g_vibeInterval = 25;
BOOL g_swipeRepeat = TRUE;
UINT g_swipeRepeatAfter = 5;
UINT g_swipeRepeatIntervalStart = 350;
UINT g_swipeRepeatInterval = 100;
BOOL g_disableOnMessageBox = TRUE;

BOOL g_hasLongClicked = FALSE;
BOOL g_touchDown = FALSE;
BOOL g_firstClick = TRUE;
HWND g_lastWindow = 0;

UINT g_singleTapTimerProc = 0;
UINT g_longTapTimerProc = 0;
UINT g_vibeTimerProc = 0;
UINT g_swipeTimerProc = 0;

UINT g_swipeCount = 0;
BOOL g_isSwipingRight = FALSE;
BOOL g_isSwiping = FALSE;

std::list<std::wstring> g_skipClasses;

void SendKey(BYTE, HWND);
BOOL ServiceMain(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hInstance);
LRESULT CALLBACK SmartTouchWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void CleanUp();
BOOL isOkayToHandle(HWND);
void CALLBACK SingleTapTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
void CALLBACK LongTapTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
void CALLBACK VibeTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
void CALLBACK SwipeTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
void DoVibeTap();
void DoVibeSwipe();
void DoSingleTap(HWND);
void DoDoubleTap(HWND);
void DoLongTap(HWND);
void LoadRegistrySettings();


#pragma region Standard Service Functions

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
  if (g_hInstance == NULL)
  {
    g_hInstance = (HINSTANCE) hModule;
  }

  switch (ul_reason_for_call)
  {
  case DLL_PROCESS_ATTACH:
  case DLL_THREAD_ATTACH:
  case DLL_THREAD_DETACH:
  case DLL_PROCESS_DETACH:
    break;
  }
  return TRUE;
}

unsigned long __cdecl MyControllingFunction(LPVOID pParam)
{
  if (!ServiceMain(g_hInstance))
  {
    CleanUp();
  }

  return 0;
}

void CleanUp()
{
  if (g_hSmartTouch && g_origWndProc) SetWindowLong(g_hSmartTouch, GWL_WNDPROC, (LONG)g_origWndProc);
  if (g_singleTapTimerProc) KillTimer(0, g_singleTapTimerProc);
  if (g_longTapTimerProc) KillTimer(0, g_longTapTimerProc);
  if (g_vibeTimerProc) KillTimer(0, g_vibeTimerProc);
  if (g_swipeTimerProc) KillTimer(0, g_swipeTimerProc);
  g_hInstance = NULL;
  g_hSmartTouch = NULL;
  g_origWndProc = NULL;
  g_singleTapTimerProc = 0;
  g_longTapTimerProc = 0;
  g_vibeTimerProc = 0;
  g_swipeTimerProc = 0;
  g_skipClasses.clear();
}

DWORD ZBP_Close(DWORD dwData)
{
  return 0;
}

DWORD ZBP_Deinit(DWORD dwData)
{
  CleanUp();

  if (g_hThread) TerminateThread(g_hThread, 1);
  g_hThread = NULL;

  return 1;
}

DWORD ZBP_Init(DWORD dwData)
{
  g_hThread = CreateThread(0, 0, MyControllingFunction, 0, 0, 0);

  return 1;
}

DWORD ZBP_IOControl(DWORD dwData, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut)
{
  return 1;
}

DWORD ZBP_Open(DWORD dwData, DWORD dwAccess, DWORD dwShareMode)
{
  return 0;
}

DWORD ZBP_Read(DWORD dwData, LPVOID pBuf, DWORD dwLen)
{
  return 0;
}

DWORD ZBP_Seek(DWORD dwData, long pos, DWORD type)
{
  return 0;
}

DWORD ZBP_Write(DWORD dwData, LPCVOID pInBuf, DWORD dwInLen)
{
  return 0;
}

#pragma endregion

#pragma region Service Work

BOOL ServiceMain(HINSTANCE hInstance)
{
  MSG msg;

  if (!InitInstance(hInstance))
  {
    return FALSE;
  }

  while (GetMessage(&msg, NULL, 0, 0)) 
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return TRUE;
}

BOOL InitInstance(HINSTANCE hInstance)
{
  // SHInitExtraControls should be called once during your application's initialization to initialize any
  // of the device specific controls such as CAPEDIT and SIPPREF.
  SHInitExtraControls();

  // Load registry
  LoadRegistrySettings();

  // find smarttouch
  do
  {
    g_hSmartTouch = FindWindow(g_szWindowClass, NULL);
    if (!g_hSmartTouch ) ::Sleep(1000); // Window not found!  Sleep a second
  } while (!g_hSmartTouch);

  // Subclass smarttouch
  do
  {
    g_origWndProc = (WNDPROC) SetWindowLong(g_hSmartTouch, GWL_WNDPROC, (LONG)SmartTouchWndProc);
    if (!g_origWndProc) ::Sleep(1000); // Window is not ready to subclass, sleep for a second
  } while (!g_origWndProc);

  return TRUE;
}

LRESULT CALLBACK SmartTouchWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  static BOOL hasSwipeVibed = FALSE;
  HWND hwndCurrent = GetForegroundWindow();

  if (isOkayToHandle(hwndCurrent))
  {
    switch (message)
    {
    case WM_USER + 209:
      // Stop both timers
      if (g_singleTapTimerProc) KillTimer(0, g_singleTapTimerProc);
      if (g_longTapTimerProc) KillTimer(0, g_longTapTimerProc);
      if (g_swipeTimerProc) KillTimer(0, g_swipeTimerProc);

      if (wParam == 0)  // Down
      {
        // Not swiping!
        g_swipeCount = 0;
        g_isSwiping = FALSE;
        g_isSwipingRight = FALSE;

        // touch
        g_touchDown = TRUE;

        // Store the message for timed events
        g_lastWindow = hwndCurrent;

        if (g_keyLTap)
        {
          g_longTapTimerProc = SetTimer(0, 0, g_longTapInterval, LongTapTimerProc);
        }
      }
      else if (wParam == 1)  // UP
      {
        hasSwipeVibed = FALSE;

        if (!g_hasLongClicked)
        {
          // release
          if (g_touchDown)
          {
            if (g_firstClick || !g_key2Tap)
            {
              if (g_key2Tap)
              {
                // Start the 'first click' timer
                g_singleTapTimerProc = SetTimer(0, 0, g_doubleTapInterval, SingleTapTimerProc);
              }
              else
              {
                // Do a single click event
                DoSingleTap(hwndCurrent);
              }
              g_firstClick = FALSE;
            }
            else // two clicks!
            {
              // Stop the 'first click' timer (just in case)
              if (g_singleTapTimerProc) KillTimer(0, g_singleTapTimerProc);
              DoDoubleTap(hwndCurrent);
              g_firstClick = TRUE;
            }
          }
        }
        else
        {
          g_hasLongClicked = FALSE;
          g_firstClick = TRUE;
        }

        g_touchDown = FALSE;
      }
      break;


    case WM_USER + 200:
      // Stop all timers (long click, first click)
      if (g_singleTapTimerProc) KillTimer(0, g_singleTapTimerProc);
      if (g_longTapTimerProc) KillTimer(0, g_longTapTimerProc);
      if (g_swipeTimerProc) KillTimer(0, g_swipeTimerProc);

      if (wParam == 0x800)
      {
        // right
        SendKey(g_keyRSwipe, g_lastWindow);
        
        if (!hasSwipeVibed) DoVibeSwipe();
        hasSwipeVibed = TRUE;

        g_touchDown = FALSE;
        g_firstClick = TRUE;

        g_isSwiping = TRUE;
        if (!g_isSwipingRight)
        {
          g_isSwipingRight = TRUE;
          g_swipeCount = 1;
        }
        else if (++g_swipeCount >= g_swipeRepeatAfter)
        {
          g_swipeTimerProc = SetTimer(0, 0, g_swipeRepeatIntervalStart, SwipeTimerProc);
        }
      }
      else if (wParam == 0x900)
      {
        // left
        SendKey(g_keyLSwipe, g_lastWindow);

        if (!hasSwipeVibed) DoVibeSwipe();
        hasSwipeVibed = TRUE;
 
        g_touchDown = FALSE;
        g_firstClick = TRUE;

        g_isSwiping = TRUE;
        if (g_isSwipingRight)
        {
          g_isSwipingRight = FALSE;
          g_swipeCount = 1;
        }
        else if (++g_swipeCount >= g_swipeRepeatAfter)
        {
          g_swipeTimerProc = SetTimer(0, 0, g_swipeRepeatIntervalStart, SwipeTimerProc);
        }
      } 
      break;
    }
  }
  else
  {
    g_touchDown = FALSE;
    g_firstClick = TRUE;
  }

  return CallWindowProc(g_origWndProc, g_hSmartTouch, message, wParam, lParam);
}



BOOL isOkayToHandle(HWND hwnd)
{
  static std::wstring dlgClass = _T("dialog");
  std::wstring curClass;
  std::list<std::wstring>::iterator it1;

  TCHAR tCurClass[MAX_PATH];

  GetClassName(hwnd,tCurClass,MAX_PATH-1);
  curClass = tCurClass;

  std::transform(curClass.begin(), curClass.end(), curClass.begin(), tolower);

  // Check to see if we are in the "MessageBox" class
  if (g_disableOnMessageBox && !dlgClass.compare(curClass))
  {
    return FALSE;
  }
  else // Check to see if we are in any of the forbidden MessageClasses
  {
    for (it1 = g_skipClasses.begin(); it1 != g_skipClasses.end(); it1++)
    {
      if (!it1->compare(curClass)) 
      {
        return FALSE;
      }
    }
  }

  return TRUE;
}


void CALLBACK SingleTapTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
  if (g_singleTapTimerProc) KillTimer(0, g_singleTapTimerProc);
  if (g_longTapTimerProc) KillTimer(0, g_longTapTimerProc);

  if (isOkayToHandle(g_lastWindow)) DoSingleTap(g_lastWindow);

  g_firstClick = TRUE;
}

void CALLBACK LongTapTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
  if (g_singleTapTimerProc) KillTimer(0, g_singleTapTimerProc);
  if (g_longTapTimerProc) KillTimer(0, g_longTapTimerProc);

  if (isOkayToHandle(g_lastWindow)) DoLongTap(g_lastWindow);
  
  g_hasLongClicked = TRUE;
  g_touchDown = FALSE;
  g_firstClick = TRUE;
}

void DoSingleTap(HWND hwnd)
{
  if (g_key1Tap > 0)
  {
    SendKey(g_key1Tap,hwnd);
    DoVibeTap();
  }
}

void DoDoubleTap(HWND hwnd)
{
  if (g_key2Tap > 0)
  {
    SendKey(g_key2Tap,hwnd);
    DoVibeTap();
  }
}

void DoLongTap(HWND hwnd)
{
  if (g_keyLTap > 0)
  {
    SendKey(g_keyLTap,hwnd);
    DoVibeTap();
  }
}


void DoVibeTap()
{
  if (g_vibeOnTap) 
  {
    if (g_vibeTimerProc) KillTimer(0, g_vibeTimerProc);
    LedOn(1);
    g_vibeTimerProc = SetTimer(0, 0, g_vibeInterval, VibeTimerProc);
  }
}

void DoVibeSwipe()
{
  if (g_vibeOnSwipe)
  {
    if (g_vibeTimerProc) KillTimer(0, g_vibeTimerProc);
    LedOn(1);
    g_vibeTimerProc = SetTimer(0, 0, g_vibeInterval, VibeTimerProc);
  }
}

void CALLBACK VibeTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
  if (g_vibeTimerProc) KillTimer(0, g_vibeTimerProc);
  LedOff(1);
}

void CALLBACK SwipeTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
  if (g_swipeTimerProc) KillTimer(0, g_swipeTimerProc);
  
  if (isOkayToHandle(g_lastWindow))
  {
    if (g_isSwipingRight)
    {
      SendKey(g_keyRSwipe,g_lastWindow);
    }
    else
    {
      SendKey(g_keyLSwipe,g_lastWindow);
    }

    g_swipeTimerProc = SetTimer(0, 0, g_swipeRepeatInterval, SwipeTimerProc);
  }
}


void LoadRegistrySettings()
{
  HKEY hk;
  DWORD disp;
  DWORD maxBytes;
  DWORD temp;

  if (!RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_SETTINGS, 0, _T(""), 0, 0, NULL, &hk, &disp))
  {
    // Keycode for single tap
    maxBytes = sizeof(temp);
    if (RegQueryValueEx(hk, _T("SingleTap"), 0, NULL, (LPBYTE) &temp, &maxBytes))
    {
      temp = 0xD;
      RegSetValueEx(hk, _T("SingleTap"), 0, REG_DWORD, (LPBYTE) &temp, maxBytes);
    }
    if (temp > 255) temp=255; if (temp < 0) temp = 0; g_key1Tap = (BYTE) temp;

    // Keycode for double tap
    maxBytes = sizeof(temp);
    if (RegQueryValueEx(hk, _T("DoubleTap"), 0, NULL, (LPBYTE) &temp, &maxBytes))
    {
      temp = 9;
      RegSetValueEx(hk, _T("DoubleTap"), 0, REG_DWORD, (LPBYTE) &temp, maxBytes);
    }
    if (temp > 255) temp=255; if (temp < 0) temp = 0; g_key2Tap = (BYTE) temp;

    // Keycode for long tap
    maxBytes = sizeof(temp);
    if (RegQueryValueEx(hk, _T("LongTap"), 0, NULL, (LPBYTE) &temp, &maxBytes))
    {
      temp = 8;
      RegSetValueEx(hk, _T("LongTap"), 0, REG_DWORD, (LPBYTE) &temp, maxBytes);
    }
    if (temp > 255) temp=255; if (temp < 0) temp = 0; g_keyLTap = (BYTE) temp;

    // Keycode for left swipe
    maxBytes = sizeof(temp);
    if (RegQueryValueEx(hk, _T("SwipeLeft"), 0, NULL, (LPBYTE) &temp, &maxBytes))
    {
      temp = 0x26;
      RegSetValueEx(hk, _T("SwipeLeft"), 0, REG_DWORD, (LPBYTE) &temp, maxBytes);
    }
    if (temp > 255) temp=255; if (temp < 0) temp = 0; g_keyLSwipe = (BYTE) temp;

    // Keycode for right swipe
    maxBytes = sizeof(temp);
    if (RegQueryValueEx(hk, _T("SwipeRight"), 0, NULL, (LPBYTE) &temp, &maxBytes))
    {
      temp = 0x28;
      RegSetValueEx(hk, _T("SwipeRight"), 0, REG_DWORD, (LPBYTE) &temp, maxBytes);
    }
    if (temp > 255) temp=255; if (temp < 0) temp = 0; g_keyRSwipe = (BYTE) temp;

    // Vibrate on tap?
    maxBytes = sizeof(temp);
    if (RegQueryValueEx(hk, _T("VibrateTap"), 0, NULL, (LPBYTE) &temp, &maxBytes))
    {
      temp = 1;
      RegSetValueEx(hk, _T("VibrateTap"), 0, REG_DWORD, (LPBYTE) &temp, maxBytes);
    }
    if (temp) {g_vibeOnTap = TRUE;} else {g_vibeOnTap = FALSE;}

    // Vibrate on swipe?
    maxBytes = sizeof(temp);
    if (RegQueryValueEx(hk, _T("VibrateSwipe"), 0, NULL, (LPBYTE) &temp, &maxBytes))
    {
      temp = 0;
      RegSetValueEx(hk, _T("VibrateSwipe"), 0, REG_DWORD, (LPBYTE) &temp, maxBytes);
    }
    if (temp) {g_vibeOnSwipe = TRUE;} else {g_vibeOnSwipe = FALSE;}

    // Vibrate interval
    maxBytes = sizeof(g_vibeInterval);
    if (RegQueryValueEx(hk, _T("VibrateInterval"), 0, NULL, (LPBYTE) &g_vibeInterval, &maxBytes))
    {
      g_vibeInterval = 25;
      RegSetValueEx(hk, _T("VibrateInterval"), 0, REG_DWORD, (LPBYTE) &g_vibeInterval, maxBytes);
    }

    // Double tap interval
    maxBytes = sizeof(g_doubleTapInterval);
    if (RegQueryValueEx(hk, _T("DoubleTapInterval"), 0, NULL, (LPBYTE) &g_doubleTapInterval, &maxBytes))
    {
      g_doubleTapInterval = 500;
      RegSetValueEx(hk, _T("DoubleTapInterval"), 0, REG_DWORD, (LPBYTE) &g_doubleTapInterval, maxBytes);
    }

    // Long tap interval
    maxBytes = sizeof(g_longTapInterval);
    if (RegQueryValueEx(hk, _T("LongTapInterval"), 0, NULL, (LPBYTE) &g_longTapInterval, &maxBytes))
    {
      g_longTapInterval = 750;
      RegSetValueEx(hk, _T("LongTapInterval"), 0, REG_DWORD, (LPBYTE) &g_longTapInterval, maxBytes);
    }

    // Do swipe repeat?
    maxBytes = sizeof(temp);
    if (RegQueryValueEx(hk, _T("SwipeDoRepeat"), 0, NULL, (LPBYTE) &temp, &maxBytes))
    {
      temp = 1;
      RegSetValueEx(hk, _T("SwipeDoRepeat"), 0, REG_DWORD, (LPBYTE) &temp, maxBytes);
    }
    if (temp) {g_swipeRepeat = TRUE;} else {g_swipeRepeat = FALSE;}

    // Do swipe repeat after this many ticks
    maxBytes = sizeof(g_swipeRepeatAfter);
    if (RegQueryValueEx(hk, _T("SwipeRepeatAfter"), 0, NULL, (LPBYTE) &g_swipeRepeatAfter, &maxBytes))
    {
      g_swipeRepeatAfter = 5;
      RegSetValueEx(hk, _T("SwipeRepeatAfter"), 0, REG_DWORD, (LPBYTE) &g_swipeRepeatAfter, maxBytes);
    }

    // First swipe repeat interval
    maxBytes = sizeof(g_swipeRepeatIntervalStart);
    if (RegQueryValueEx(hk, _T("SwipeRepeatIntervalFirst"), 0, NULL, (LPBYTE) &g_swipeRepeatIntervalStart, &maxBytes))
    {
      g_swipeRepeatIntervalStart = 350;
      RegSetValueEx(hk, _T("SwipeRepeatIntervalFirst"), 0, REG_DWORD, (LPBYTE) &g_swipeRepeatIntervalStart, maxBytes);
    }

    // First swipe repeat interval
    maxBytes = sizeof(g_swipeRepeatInterval);
    if (RegQueryValueEx(hk, _T("SwipeRepeatIntervalRate"), 0, NULL, (LPBYTE) &g_swipeRepeatInterval, &maxBytes))
    {
      g_swipeRepeatInterval = 100;
      RegSetValueEx(hk, _T("SwipeRepeatIntervalRate"), 0, REG_DWORD, (LPBYTE) &g_swipeRepeatInterval, maxBytes);
    }

    // Do we disable when the MessageBox Class is the foreground window?
    maxBytes = sizeof(temp);
    if (RegQueryValueEx(hk, _T("DisableOnMessageBox"), 0, NULL, (LPBYTE) &temp, &maxBytes))
    {
      temp = 1;
      RegSetValueEx(hk, _T("DisableOnMessageBox"), 0, REG_DWORD, (LPBYTE) &temp, maxBytes);
    }
    if (temp) {g_disableOnMessageBox = TRUE;} else {g_disableOnMessageBox = FALSE;}

    // Close the registry
    RegCloseKey(hk);
  }

  // Enumerate all the classes being handled by the zoom
  //MessageBox(0,L"1",L"",MB_TOPMOST);
  if (!RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("Software\\HTC\\SmartTouch"),0,0,&hk))
  {
    //MessageBox(0,L"2",L"",MB_TOPMOST);
    DWORD index = 0;
    TCHAR appName[MAX_PATH];
    DWORD size = MAX_PATH - 1;
    TCHAR regClass[MAX_PATH];
    HKEY hk2;

    while (!RegEnumKeyEx(hk,index,appName,&size,0,0,0,0))
    {
      //MessageBox(0,L"3",L"",MB_TOPMOST);
      if (!RegOpenKeyEx(hk,appName,0,0,&hk2))
      {
        //MessageBox(0,L"4",L"",MB_TOPMOST);
        std::wstring wsRegClass;
        size = sizeof(TCHAR) * (MAX_PATH - 1);
        if (!RegQueryValueEx(hk2,_T("ClassName"),0,0,(LPBYTE)regClass,&size))
        {
          wsRegClass = regClass;
          std::transform(wsRegClass.begin(), wsRegClass.end(), wsRegClass.begin(), tolower);
          g_skipClasses.push_back(wsRegClass);
        }
        RegCloseKey(hk2);
      }
      size = MAX_PATH - 1;
      index ++;
    }
    RegCloseKey(hk);
  }
}

void SendKey(BYTE key, HWND hwnd)
{
  if (hwnd == GetForegroundWindow())
  {
    keybd_event(key, 0, 0, 0);
    keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
  }
}

#pragma endregion